Skip to content

feat & fix : 쿠폰 조회 버그 수정, 챗봇 기능 확장, 서킷브레이커 적용, email 알림 코드 삭제 - #72

Merged
jaebeom79 merged 8 commits into
mainfrom
develop
May 18, 2026
Merged

feat & fix : 쿠폰 조회 버그 수정, 챗봇 기능 확장, 서킷브레이커 적용, email 알림 코드 삭제#72
jaebeom79 merged 8 commits into
mainfrom
develop

Conversation

@silkair

@silkair silkair commented May 18, 2026

Copy link
Copy Markdown
Contributor

📢 기능 설명

필요시 실행결과 스크린샷 첨부

연결된 issue

연결된 issue를 자동을 닫기 위해 아래 {이슈넘버}를 입력해주세요.

close #{이슈넘버}

✅ 체크리스트

  • PR 제목 규칙 잘 지켰는가?
  • 추가/수정사항을 설명하였는가?
  • 이슈넘버를 적었는가?

johe00123 and others added 8 commits May 15, 2026 22:35
- 취소/노쇼 예약 내역 조회 AI Tool 추가
- 주변 인기 맛집 추천 AI Tool 추가 (반경 3km, 인기순 정렬)
- 예약 가능 시간대 조회 AI Tool 추가
- 챗봇 요청에 위치 좌표(latitude/longitude) 필드 추가
- 쿠폰 목록 API Cache-Control: no-store 추가
- 챗봇 대화 내역 조회 시 세션 없으면 빈 목록 반환으로 수정
- SecurityConfig ASYNC DispatcherType 설정 제거

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- N+1 쿼리 제거: PENDING orderId 조회 IN 쿼리 1회로 교체 (PaymentRepository)
- DB 레벨 필터링: findAllByUserAndStatusIn 추가로 메모리 필터 제거 (ReservationRepository)
- DB 레벨 매장 조회: findByStoreNameIgnoreCaseAndIsDeletedFalse 추가 (StoreRepository)
- ThreadLocal 누수 방지: PendingPaymentHolder.clear() try-finally로 보장 (ChatbotService)
- 챗봇 시스템 프롬프트 가드레일 6항목 추가 (역할고정, 범위제한, 프롬프트보호, 타인정보보호, 역할극거부, 개인정보수집금지)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactor/#66 chatbot api
- build.gradle: resilience4j-spring-boot3 의존성 추가
- application.yaml: ai-api 서킷브레이커 설정 추가
  (10회 슬라이딩 윈도우, 실패율 50% OPEN, 30초 대기, HALF-OPEN 3회 테스트)
- ChatbotService: callAi() 내부에 CircuitBreaker.executeSupplier() 프로그래매틱 적용
- ErrorCode: CHAT_AI_CIRCUIT_OPEN(503) 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: AI API 서킷브레이커 적용 (Resilience4j)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the chatbot system by integrating Resilience4j for circuit breaking, implementing conversation history summarization, and adding several AI-driven tools for store searching, reservation management, and location-based recommendations. It also introduces a ThreadLocal holder for pending payment information and optimizes reservation list retrieval with batch queries. Feedback focuses on improving system stability by applying the circuit breaker to the summarization logic and ensuring timezone consistency in the system prompt. Additionally, it is recommended to include store IDs in AI tool responses to support the link format requested in the prompt and to refactor duplicated orderId generation logic into a shared method.

Comment on lines 188 to +193
try {
return chatClient.prompt()
.messages(messages)
.tools(reservationService, couponService)
.toolContext(Map.of("userId", userId))
String summary = chatClient.prompt()
.user("다음 대화를 핵심 정보(예약 정보, 사용자 선호 등)만 3문장 이내로 요약해줘:\n" + historyText)
.call()
.content();

return "\n\n[이전 대화 요약]: " + summary;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

buildSummarySuffix 메서드에서 AI를 호출할 때 서킷 브레이커가 적용되지 않았습니다. AI 서비스에 장애가 발생하거나 응답이 느려질 경우, 메인 로직인 callAi에 도달하기 전에 이 부분에서 요청이 지연되거나 실패할 수 있습니다. 요약 기능에도 aiCircuitBreaker를 적용하여 시스템의 안정성을 높이는 것이 좋습니다.

        try {
            return aiCircuitBreaker.executeSupplier(() -> {
                String summary = chatClient.prompt()
                        .user("다음 대화를 핵심 정보(예약 정보, 사용자 선호 등)만 3문장 이내로 요약해줘:\n" + historyText)
                        .call()
                        .content();
                return "\n\n[이전 대화 요약]: " + summary;
            });
        } catch (CallNotPermittedException e) {
            return "";
        } catch (Exception e) {


private String buildSystemPrompt(String summarySuffix) {
return "너는 'CatchEat(캐치잇)'이라는 레스토랑 예약 플랫폼의 AI 비서야. "
+ "오늘 날짜는 " + java.time.LocalDate.now() + "이야. "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

LocalDate.now()는 서버의 기본 시간대를 사용합니다. ChatbotDbService에서는 Asia/Seoul 시간대를 명시적으로 사용하고 있으므로, 시스템 프롬프트 생성 시에도 동일한 시간대를 사용하여 일관성을 유지하는 것이 좋습니다.

Suggested change
+ "오늘 날짜는 " + java.time.LocalDate.now() + "이야. "
+ "오늘 날짜는 " + java.time.LocalDate.now(java.time.ZoneId.of("Asia/Seoul")) + "이야. "

Comment on lines +149 to +158
return reservations.stream().map(r -> {
StoreRemain sr = r.getStoreRemain();
return String.format("예약번호 %d: %s %s %s, %d명, 상태: %s",
r.getId(),
sr.getStore().getStoreName(),
sr.getRemainDate(),
sr.getRemainTime(),
r.getMember(),
r.getStatus() == ReservationStatus.PENDING ? "결제 대기" : "확정");
}).collect(Collectors.joining("\n"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

시스템 프롬프트(ChatbotService)에서는 매장 이름을 링크 형식([매장이름](/stores/{storeId}))으로 작성하도록 지시하고 있지만, getMyReservationsForAi 도구의 응답에는 storeId 정보가 포함되어 있지 않습니다. AI가 정확한 링크를 생성할 수 있도록 응답 문자열에 매장 ID 정보를 포함하는 것이 좋습니다. getCanceledReservationsForAi 메서드도 동일한 개선이 필요해 보입니다.

        return reservations.stream().map(r -> {
            StoreRemain sr = r.getStoreRemain();
            return String.format("예약번호 %d: [%s](/stores/%d) %s %s, %d명, 상태: %s",
                    r.getId(),
                    sr.getStore().getStoreName(),
                    sr.getStore().getId(),
                    sr.getRemainDate(),
                    sr.getRemainTime(),
                    r.getMember(),
                    r.getStatus() == ReservationStatus.PENDING ? "결제 대기" : "확정");
        }).collect(Collectors.joining("\n"));

storeRemain.getRemainTime().toString()
));
// Payment 레코드 생성 (결제창 호출을 위해 orderId 필요)
String orderId = "CATCH-" + saved.getId() + "-" + System.currentTimeMillis();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

orderId를 생성하는 로직("CATCH-" + saved.getId() + "-" + System.currentTimeMillis())이 create 메서드(208라인)와 중복되어 사용되고 있습니다. 중복 코드는 유지보수 시 실수를 유발할 수 있으므로, 별도의 프라이빗 메서드로 분리하여 관리하는 것을 권장합니다.

@jaebeom79
jaebeom79 merged commit df08a26 into main May 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants